package com.apes.scm.account.account.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.logger.ApesLoggerService;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.plugin.outbox.OutboxPayloadWrapper;
import com.apes.framework.plugin.outbox.OutboxSubscriberService;
import com.apes.framework.plugin.outbox.annotation.OutboxSubscriber;
import com.apes.framework.util.MapUtil;
import com.apes.framework.util.Tools;
import com.apes.pi.service.PiManager;
import com.apes.scm.account.account.model.*;
import com.apes.scm.account.account.repository.*;
import com.apes.scm.account.transit.DepositInTransitService;
import com.apes.scm.masterdata.contrast.service.ContrastPartyCodeService;
import com.apes.scm.masterdata.party.model.PartyRole;
import com.apes.scm.masterdata.party.repository.PartyRoleRepository;
import com.apes.scm.masterdata.product.model.Classify;
import com.apes.scm.masterdata.product.model.Product;
import com.apes.scm.masterdata.product.model.ProductUom;
import com.apes.scm.masterdata.product.repository.ProductRepository;
import com.apes.scm.masterdata.product.repository.ProductUomRepository;
import com.apes.scm.masterdata.product.service.CategorySupplyRangeService;
import com.apes.scm.masterdata.product.service.ProductUomService;
import com.apes.scm.masterdata.product.service.UomService;
import com.apes.scm.masterdata.stock.model.Location;
import com.apes.scm.masterdata.stock.repository.LocationRepository;
import com.apes.scm.stock.safetyStock.model.SafetyStockGap;
import com.apes.scm.stock.safetyStock.repository.SafetyStockGapRepository;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * Created by HuJianlin on 2018-01-27.
 * 更新商品库存帐 Service
 */
@Service("productAccountService")
public class ProductAccountService extends DomainService {

    @Autowired
    StockRepository stockRepository;
    @Autowired
    DetailOfUsableRepository detailOfUsableRepository;
    @Autowired
    DetailOfCustodyRepository detailOfCustodyRepository;
    @Autowired
    DetailOfFrozenRepository detailOfFrozenRepository;
    @Autowired
    DetailOfLendRepository detailOfLendRepository;
    @Autowired
    UomService uomService;
    @Autowired
    ProductRepository productRepository;
    @Autowired
    ProductUomRepository productUomRepository;
    @Autowired
    private EntityManager entityManager;
    @Autowired
    SafetyStockGapRepository safetyStockGapRepository;
    @Autowired
    private PiManager piManager;
    @Autowired
    private DepositInTransitService depositInTransitService;
    @Autowired
    private CategorySupplyRangeService categorySupplyRangeService;
    @Autowired
    private LocationRepository locationRepository;
    @Autowired
    private ProductUomService productUomService;
    @Autowired
    private ContrastPartyCodeService contrastPartyCodeService;
    @Autowired
    private DetailOfRequireRepository detailOfRequireRepository;
    @Autowired
    private SafetyStockLoggerRepository safetyStockLoggerRepository;
    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 写商品库存帐可用数
     *
     * @param product               商品
     * @param location              仓库库区
     * @param changeQty             数量
     * @param sourceBusinessType    单据类型
     * @param orderNumber           订单号
     * @param orderSerialNumber     订单明细行号
     * @param remarks               备注
     * @param company               公司
     */
    public void setProductAvailable(Product product, Location location, ProductUom uom, double changeQty,
                                    String sourceBusinessType, String orderNumber,
                                    String orderSerialNumber, String remarks, PartyRole company) {
        changeQty = computeQtyUnit(company, product, uom, changeQty);

        Stock stock = getOrCreateProductAccount(product, location, company);
        stock.addUsableQty(changeQty);
        stock = stockRepository.saveAndFlush(stock);

        DetailOfUsable detailOfUsable = new DetailOfUsable();
        detailOfUsable.setProduct(product);
        detailOfUsable.setLocation(location);
        detailOfUsable.setCompany(company);
        detailOfUsable.setChangeQty(changeQty);
        detailOfUsable.setSourceBusinessType(sourceBusinessType);
        detailOfUsable.setOrderNumber(orderNumber);
        detailOfUsable.setOrderSerialNumber(orderSerialNumber);
        detailOfUsable.setRemarks(remarks);
        detailOfUsable.setBalanceQty(stock.getUsableQty());
        detailOfUsable = detailOfUsableRepository.saveAndFlush(detailOfUsable);
        depositInTransitService.save(detailOfUsable);
    }

    /**
     * 写商品库存帐可用数(支持超卖)
     *
     */
    public void setProductAvailable(Product product, Location location, ProductUom uom, double changeQty,
                                    String sourceBusinessType, String orderNumber,
                                    String orderSerialNumber, String remarks, PartyRole company,String overSale) {
        changeQty = computeQtyUnit(company, product, uom, changeQty);

        Stock stock = getOrCreateProductAccount(product, location, company);
        stock.addOverUsableQty(changeQty);
        stock = stockRepository.saveAndFlush(stock);

        DetailOfUsable detailOfUsable = new DetailOfUsable();
        detailOfUsable.setProduct(product);
        detailOfUsable.setLocation(location);
        detailOfUsable.setCompany(company);
        detailOfUsable.setChangeQty(changeQty);
        detailOfUsable.setSourceBusinessType(sourceBusinessType);
        detailOfUsable.setOrderNumber(orderNumber);
        detailOfUsable.setOrderSerialNumber(orderSerialNumber);
        detailOfUsable.setRemarks(remarks);
        detailOfUsable.setBalanceQty(stock.getUsableQty());
        detailOfUsable = detailOfUsableRepository.saveAndFlush(detailOfUsable);
        depositInTransitService.save(detailOfUsable);
    }

    private Stock getOrCreateProductAccount(Product product, Location location, PartyRole company) {
        Stock stock = stockRepository.findByProductAndLocation(product, location);
        if (stock == null) {
            stock = new Stock();
            stock.setProduct(product);
            stock.setLocation(location);
            stock.setCompany(location.getCompany());
        }
        return stock;
    }


    /**
     * 功能：计算基本单位数量
     * @param company
     * @param product
     * @param uom
     * @param changeQty
     * @return
     */
    public double computeQtyUnit(PartyRole company, Product product, ProductUom uom, double changeQty) {
        ProductUom baseUom = uomService.getUom(company, product);
        if(baseUom == null) throw new RuntimeException("商品：" + product.getName() + " 没有定义基本计量单位！");
        if (uom.getId() != baseUom.getId()) {
            return uom.computeQty(changeQty, baseUom, true);
        }
        return changeQty;
    }

    /**
     * 写商品库存帐保管数
     *
     * @param product
     * @param location
     * @param changeQty
     * @param sourceBusinessType
     * @param orderNumber
     * @param orderSerialNumber
     * @param remarks
     * @param company
     */
    public void setProductCustody(Product product, Location location, ProductUom uom, double changeQty,
                                  String sourceBusinessType, String orderNumber,
                                  String orderSerialNumber, String remarks, PartyRole company) {
        changeQty = computeQtyUnit(company, product, uom, changeQty);

        Stock stock = getOrCreateProductAccount(product, location, company);
        stock.addCustodyQty(changeQty);
        stock = stockRepository.saveAndFlush(stock);

        DetailOfCustody accDetailOfCustody = new DetailOfCustody();
        accDetailOfCustody.setProduct(product);
        accDetailOfCustody.setLocation(location);
        accDetailOfCustody.setCompany(company);
        accDetailOfCustody.setChangeQty(changeQty);
        accDetailOfCustody.setSourceBusinessType(sourceBusinessType);
        accDetailOfCustody.setOrderNumber(orderNumber);
        accDetailOfCustody.setOrderSerialNumber(orderSerialNumber);
        accDetailOfCustody.setRemarks(remarks);
        accDetailOfCustody.setBalanceQty(stock.getCustodyQty());
        accDetailOfCustody = detailOfCustodyRepository.saveAndFlush(accDetailOfCustody);
        depositInTransitService.save(accDetailOfCustody);
    }

    /**
     * 写商品库存帐待检数
     *
     * @param product
     * @param location
     * @param changeQty
     * @param company
     */
    public void setProductWaitTest(Product product, Location location, ProductUom uom, double changeQty, PartyRole company) {
        changeQty = computeQtyUnit(company, product, uom, changeQty);
        Stock stock = getOrCreateProductAccount(product, location, company);
        double qty = stock.getWaitTestQty() + changeQty;
        stock.setWaitTestQty(qty);
        stockRepository.saveAndFlush(stock);
    }

    /**
     * 写商品库存帐冻结数
     *
     * @param product
     * @param location
     * @param changeQty
     * @param company
     * @param sourceType 填单据最原始来源类型
     * @param source 填单据最原始来源单号
     * @param sourceNumber 填明细单据最原始来源单号  这3个参数传参前自己溯源
     */
    public void setProductFrozen(Product product, Location location, ProductUom uom, double changeQty,
                                 String sourceBusinessType, String orderNumber,
                                 String orderSerialNumber, String remarks, PartyRole company,String sourceType,String source,String sourceNumber) {
        long time = new Date().getTime();
        writeDB("accDetailOfFrozen", orderNumber, mapper(
                "库区编码", location.getId(), "商品编码", product.getId(),
                "当前时间", LocalDate.now(),
                "单据序号", orderSerialNumber, "变动数量", changeQty
        ), "冻结数记录:" + time);
        changeQty = computeQtyUnit(company, product, uom, changeQty);
        Stock stock = getOrCreateProductAccount(product, location, company);
        try {
            stock.addFrozenQty(changeQty);
        } catch (Exception e) {
            writeDB("accDetailOfFrozen", orderNumber, mapper(
                    "库区编码", location.getId(), "商品编码", product.getId(), "单据序号", orderSerialNumber,
                    "备注", remarks, "计量单位", uom.getId() + "/" + uom.getName(), "当前时间", LocalDate.now(),
                    "变动数量", changeQty, "库存帐冻结数", stock.getFrozenQty(), "异常信息", e.getMessage()
            ), "抛异常step1:" + time);
            throw new RuntimeException(e.getMessage() + ",错误代码:" + time);
        }
        stockRepository.saveAndFlush(stock);

        DetailOfFrozen accDetailOfFrozen = new DetailOfFrozen();
        accDetailOfFrozen.setProduct(product);
        accDetailOfFrozen.setLocation(location);
        accDetailOfFrozen.setCompany(company);
        accDetailOfFrozen.setChangeQty(changeQty);
        accDetailOfFrozen.setSourceBusinessType(sourceBusinessType);
        accDetailOfFrozen.setOrderNumber(orderNumber);
        accDetailOfFrozen.setOrderSerialNumber(orderSerialNumber);
        accDetailOfFrozen.setRemarks(remarks);
        accDetailOfFrozen.setBalanceQty(stock.getFrozenQty());

        accDetailOfFrozen.setSourceType(sourceType);
        accDetailOfFrozen.setSource(source);
        accDetailOfFrozen.setSourceNumber(sourceNumber);
        detailOfFrozenRepository.saveAndFlush(accDetailOfFrozen);
        if (Objects.equals(sourceBusinessType, sourceType) && Objects.equals(orderNumber, source) && Objects.equals(orderSerialNumber, sourceNumber) && changeQty > 0) {
            return;
        }
        try {
            DetailOfFrozen detailOfFrozen = detailOfFrozenRepository.findOne(JpaDsl.toCriteria("sourceBusinessType", "EQ", sourceType, "orderNumber", "EQ", source, "orderSerialNumber", "EQ", sourceNumber, "changeQty", "GT", 0)).orElse(null);
            if (Objects.nonNull(detailOfFrozen)){
                detailOfFrozen.addThawingQty(changeQty * -1);
                detailOfFrozenRepository.saveAndFlush(detailOfFrozen);
            }
        } catch (Exception e) {
            throw new RuntimeException(String.format("生成冻结数明细帐异常！未找到源单据的冻结数明细账！[%s][%s][%s][%s]", sourceType, source,sourceNumber,e.getMessage()));
        }

    }


    private void writeDB(String collectionName,String order, Map data,String note){
        if (false)return;
        Document document = new Document();
        document.put("order", order);
        document.put("data", data);
        document.put("note",note);
        mongoTemplate.getCollection(collectionName).insertOne(document);
    }

    /**
     * 写商品库存帐销售控制数
     *
     * @param product
     * @param location
     * @param changeQty
     * @param company
     */
    public void setProductSalesControl(Product product, Location location, ProductUom uom, double changeQty, PartyRole company) {
        changeQty = computeQtyUnit(company, product, uom, changeQty);
        Stock stock = getOrCreateProductAccount(product, location, company);
        double qty = stock.getSalesControlQty() + changeQty;
        stock.setSalesControlQty(qty);
        stockRepository.saveAndFlush(stock);
    }

    /**
     * 写商品库存帐转储发货在途
     *
     * @param product
     * @param location
     * @param changeQty
     * @param company
     */
    public void setProductTransferIssue(Product product, Location location, ProductUom uom, double changeQty, PartyRole company) {
        changeQty = computeQtyUnit(company, product, uom, changeQty);
        Stock stock = getOrCreateProductAccount(product, location, company);
        double qty = stock.getTransferIssueQty() + changeQty;
        stock.setTransferIssueQty(qty);
        stockRepository.saveAndFlush(stock);
    }

    /**
     * 写商品库存帐转储收货在途
     *
     * @param product
     * @param location
     * @param changeQty
     * @param company
     */
    public void setProductTransferReceive(Product product, Location location, ProductUom uom, double changeQty, PartyRole company) {
        changeQty = computeQtyUnit(company, product, uom, changeQty);
        Stock stock = getOrCreateProductAccount(product, location, company);
        double qty = stock.getTransferReceiveQty() + changeQty;
        stock.setTransferReceiveQty(qty);
        stockRepository.saveAndFlush(stock);
    }

    /**
     * 写商品库存帐订货在途
     *
     * @param product
     * @param location
     * @param changeQty
     * @param company
     */
    public void setProductPurchasingWay(Product product, Location location, ProductUom uom, double changeQty, PartyRole company) {
        changeQty = computeQtyUnit(company, product, uom, changeQty);
        Stock stock = getOrCreateProductAccount(product, location, company);
        double qty = stock.getPurchasingWayQty() + changeQty;
        stock.setPurchasingWayQty(qty);
        stockRepository.saveAndFlush(stock);
    }

    /**
     * 写商品库存帐借还（领用）在途
     *
     * @param product
     * @param location
     * @param person
     * @param changeQty
     * @param lendType
     * @param company
     */
    public void setProductLend(Product product, Location location, PartyRole person, ProductUom uom, double changeQty, String lendType, PartyRole company) {
        changeQty = computeQtyUnit(company, product, uom, changeQty);
        Stock stock = getOrCreateProductAccount(product, location, company);
        double qty = stock.getLendQty() + changeQty;
        stock.setLendQty(qty);
        stockRepository.saveAndFlush(stock);

        int iCount = detailOfLendRepository.update(product.getId(), location.getId(), person.getId(), changeQty);
        if (iCount == 0) {
            DetailOfLend detailOfLend = new DetailOfLend();
            detailOfLend.setProduct(product);
            detailOfLend.setLocation(location);
            detailOfLend.setLendType(lendType);
            detailOfLend.setPerson(person);
            detailOfLend.setLendQty(changeQty);
            detailOfLend.setCompany(company);
            detailOfLendRepository.saveAndFlush(detailOfLend);
        }
    }

    /**
     * 写商品库存帐采购订货制单数量
     *
     * @param product
     * @param location
     * @param changeQty
     * @param company
     */
    public void setProductPurchaseBilling(Product product, Location location, ProductUom uom, double changeQty, PartyRole company) {
        changeQty = computeQtyUnit(company, product, uom, changeQty);
        Stock stock = getOrCreateProductAccount(product, location, company);
        double qty = stock.getPurchaseBillingQty() + changeQty;
        stock.setPurchaseBillingQty(qty);
        stockRepository.saveAndFlush(stock);
    }

    /**
     * 写商品库存帐转入制单数量
     *
     * @param product
     * @param location
     * @param changeQty
     * @param company
     */
    public void setProductTransferInBilling(Product product, Location location, ProductUom uom, double changeQty, PartyRole company) {
        changeQty = computeQtyUnit(company, product, uom, changeQty);
        Stock stock = getOrCreateProductAccount(product, location, company);
        double qty = stock.getTransferInBillingQty() + changeQty;
        if (qty < 0) {
            qty = 0;
        }
        stock.setTransferInBillingQty(qty);
        stockRepository.saveAndFlush(stock);
    }

    /***
     * 写入库存帐要货制单数量
     * @param product
     * @param location
     * @param changeQty
     * @param company
     */
    public void setProductRequireBilling(Product product, Location location, ProductUom uom, double changeQty,
                                         String sourceBusinessType, String orderNumber,
                                         String orderSerialNumber, String remarks, PartyRole company, String productRequire) {

        changeQty = computeQtyUnit(company, product, uom, changeQty);
        Stock stock = getOrCreateProductAccount(product, location, company);
        stock.addRequireBillingQty(changeQty);
        stockRepository.saveAndFlush(stock);

        DetailOfRequire accDetailOfRequire = new DetailOfRequire();
        accDetailOfRequire.setProduct(product);
        accDetailOfRequire.setLocation(location);
        accDetailOfRequire.setCompany(company);
        accDetailOfRequire.setChangeQty(changeQty);
        accDetailOfRequire.setSourceBusinessType(sourceBusinessType);
        accDetailOfRequire.setOrderNumber(orderNumber);
        accDetailOfRequire.setOrderSerialNumber(orderSerialNumber);
        accDetailOfRequire.setRemarks(remarks);
        accDetailOfRequire.setBalanceQty(stock.getRequireBillingQty());

        accDetailOfRequire.setProductRequire(productRequire);
        detailOfRequireRepository.saveAndFlush(accDetailOfRequire);
    }

    /**
     * 写商品库存帐安全库存
     *
     * @param product
     * @param location
     * @param theMaxQty
     * @param theMinQty
     * @param safetyStockQty
     * @param company
     */
    public Stock setProductSafetyStock(Product product, Location location, ProductUom uom, double theMaxQty,
                                      double theMinQty, double safetyStockQty, PartyRole company) {
        theMaxQty = computeQtyUnit(company, product, uom, theMaxQty);
        theMinQty = computeQtyUnit(company, product, uom, theMinQty);
        safetyStockQty = computeQtyUnit(company, product, uom, safetyStockQty);
        Stock stock = getOrCreateProductAccount(product, location, company);
        //数量不能叠加更新
        // double qty = stock.getTheMaxQty() + theMaxQty;
        // stock.setTheMaxQty(qty);
        // qty = stock.getTheMinQty() + theMinQty;
        // stock.setTheMinQty(qty);
        // qty = stock.getSafetyStockQty() + safetyStockQty;
        // stock.setSafetyStockQty(qty);
        //只能对“品类直供范围”或“自采范围”内的一般商品（有库存管理的）设置安全库存。
  /*      Classify classify = product.getClassify();
        if (classify != null ) {
            if (!categorySupplyRangeService.checkInRange(classify,product.getBrand()) ) {
                throw new RuntimeException("商品"+product.getId()+"不属于品类直供范围或自采范围内,不能设置安全库存!!!");
            }
        }else {
            throw new RuntimeException("商品"+product.getId()+"没有商品分类信息,请去补充完成!!");
        }*/
        stock.setSafetyStockQty(safetyStockQty);
        stock.setTheMinQty(theMinQty);
        stock.setTheMaxQty(theMaxQty);
        return stockRepository.saveAndFlush(stock);
    }
    /**
     * 查询商品可用数
     * @param request
     * @return
     */
    public Stock getUsableQtyByProductAndLocation(SimpleRequest request){
        JSONObject params = request.getJO();

        Location location = new Location();
        location.setId( params.getString("locationId") );
        Product product = new Product();
        product.setId( params.getString("productId") );

        return stockRepository.findByProductAndLocation(product, location);
    }

    /***
     * 获取可用数大于零的商品（不管冻结数,记得传入仓库，否则存在多条相同的商品）
     */
    public Page<Map> findAllUsableProduct(SimpleRequest request) {
        Page<Stock> stocks = stockRepository.findAll(JpaDsl.getGroup(request, originGroup -> {
            // 可用数大于零
            originGroup.addFilters(JpaDsl.toFilter(
                    "usableQty", "GT", 0
            ));
            return originGroup;
        }).build(true),  JpaDsl.getPageable(request));
        List<Map>  productList = stocks.getContent().stream().map(stock -> {
            // 获取一个转储默认单位，将可用数转化为当前的单位
            ProductUom uom =  stock.getProduct().queryTransferFirstUom().get(0);
            Map usableMap = getUomUsableQty(stock.getLocation(), stock.getProduct(), uom, stock.getLocation().getCompany());
            Map m = MapUtil.mapped(stock.getProduct());
            double marketableQty = Double.parseDouble(usableMap.get("marketableQty").toString());
            int marketableQtyI = (int) marketableQty; // 有小数时取整，否则默认可转储数量、计量单位为库存可用数、库存单位
            m.put("usableQty", marketableQtyI > 0?marketableQtyI:usableMap.get("normalUsableQty"));
            m.put("uom", marketableQtyI > 0?uom:usableMap.get("stockUom"));
            return m;
        }).collect(Collectors.toList());

        return new PageImpl<>(productList, stocks.getPageable(), stocks.getTotalElements());
    }


    public Map getUsableQtyToUom(SimpleRequest request) {
        JSONObject tempJson = request.getJO();
        Location location = new Location();
        PartyRole company = new PartyRole();
        Product product = productRepository.findOne(tempJson.getJSONObject("product").getString("id"));
        ProductUom uom = productUomRepository.findOne(tempJson.getJSONObject("uom").getLong("id"));
        location.setId(tempJson.getJSONObject("location").getString("id"));
        company.setId(tempJson.getJSONObject("company").getString("id"));
        return getUsableQtyToUom(location, product, uom, company);
    }

    /**
     * 功能：通过仓库和商品获取可用库存并进行目标单位的换算
     * 获取可卖数（减去冻结数）
     * @param location
     * @param product
     * @param uom
     * @param company
     * @return
     */
    public Map getUsableQtyToUom(Location location, Product product, ProductUom uom, PartyRole company){
        Map returnMap = new HashMap();
        List<Stock> stockList = stockRepository.findAll(JpaDsl.toCriteria(MapUtil.mapper("location", location, "product", product)));
        if( stockList != null && stockList.size() > 0){
            Stock stock = stockList.get(0);
            // 有需要自行添加
            returnMap.put("oos", stock.isOos());
            returnMap.put("marketableQty",uomService.getUom(company, product).computeQty(stockList.get(0).getMarketableQty(), uom, true));
        }
        return returnMap;
    }

    public Map getUomUsableQty(SimpleRequest request) {
        JSONObject tempJson = request.getJO();
        Location location = new Location();
        PartyRole company = new PartyRole();
        Product product = productRepository.findOne(tempJson.getJSONObject("product").getString("id"));
        ProductUom uom = productUomRepository.findOne(tempJson.getJSONObject("uom").getLong("id"));
        location.setId(tempJson.getJSONObject("location").getString("id"));
        company.setId(tempJson.getJSONObject("company").getString("id"));
        return getUomUsableQty(location, product, uom, company);
    }

    /**
     * 功能：通过仓库和商品获取可用库存并进行目标单位的换算
     * 获取可用数（未减去冻结数）
     * @param location 库区
     * @param product 商品
     * @param uom 单位
     * @param company 公司
     * @return map
     */
    public Map getUomUsableQty(Location location, Product product, ProductUom uom, PartyRole company) {
        Map returnMap = new HashMap();
        List<Stock> stockList = stockRepository.findAll(JpaDsl.toCriteria(MapUtil.mapper("location", location, "product", product)));
        if (stockList != null && stockList.size() > 0) {
            Stock stock = stockList.get(0);
            // 有需要自行添加
            returnMap.put("oos", stock.isOos());
            returnMap.put("marketableQty", uomService.getUom(company, product).computeQty(stockList.get(0).getUsableQty(), uom, true));
            returnMap.put("normalUsableQty", stockList.get(0).getUsableQty());
            returnMap.put("stockUom", stockList.get(0).getProduct().getUom());
        }
        return returnMap;
    }

    /**
     * 更新商品库存帐安全库存
     *
     */
    public Stock updateProductSafetyStock(SimpleRequest request) {
        Stock stock = request.getO(Stock.class);
        if (stock.getSafetyStockQty() < 0 || stock.getTheMaxQty() < 0 || stock.getTheMinQty() < 0) {
            throw new RuntimeException("数据不能有负数不能小于0");
        }
        if (stock.getSafetyStockQty() < stock.getTheMinQty()) {
            throw new RuntimeException("安全库存不能低于最低限量");
        }
        /**记录变动记录**/
        List<Stock> historyStocks = stockRepository.findAll(JpaDsl.toCriteriaByEq("location.id", stock.getLocation().getId(), "product.id", stock.getProduct().getId()));
        if (historyStocks.size() > 0) createSafetyStockLogger(historyStocks.get(0), stock, getPerson(request));
        return setStockSafety(stock);
    }

    /**
     * s设置安全库存
     * @param stock
     * @return
     */
    private Stock setStockSafety(Stock stock) {
        Product product = stock.getProduct();
        try {
            ProductUom uom = product.getUom();
            Location location = stock.getLocation();
            PartyRole company = stock.getCompany();
            if (company == null) {
                company = location.getCompany();
            }
            double theMinQty = stock.getTheMinQty();
            double theMaxQty = stock.getTheMaxQty();
            double safetyStockQty = stock.getSafetyStockQty();
            return  setProductSafetyStock(product,location,uom,theMaxQty,theMinQty,safetyStockQty,company);
        } catch (Exception e) {
            String msg = e.toString();
            if (msg.indexOf("商品") != -1) {
                throw  e;
            }else {
                throw new RuntimeException("商品存在问题,编码:"+product.getId() +"错误信息:"+ e);
            }
        }
    }

    /**
     * 记录安全库存变动
     */
    private void createSafetyStockLogger(Stock oldStock,Stock newStock,PartyRole changeUid) {
        SafetyStockLogger safetyStockLogger = new SafetyStockLogger();
        safetyStockLogger.setProduct(oldStock.getProduct());
        safetyStockLogger.setLocation(oldStock.getLocation());
        safetyStockLogger.setLastSafetyStockQty(oldStock.getSafetyStockQty());
        safetyStockLogger.setLastTheMaxQty(oldStock.getTheMaxQty());
        safetyStockLogger.setLastTheMinQty(oldStock.getTheMinQty());
        if (newStock == null) {
            safetyStockLogger.setSafetyStockQty(0);
            safetyStockLogger.setTheMaxQty(0);
            safetyStockLogger.setTheMinQty(0);
        }else {
            safetyStockLogger.setSafetyStockQty(newStock.getSafetyStockQty());
            safetyStockLogger.setTheMaxQty(newStock.getTheMaxQty());
            safetyStockLogger.setTheMinQty(newStock.getTheMinQty());
        }

        safetyStockLogger.setChangeUid(changeUid);
        safetyStockLogger.setChangeDate(new Date());
        safetyStockLoggerRepository.saveAndFlush(safetyStockLogger);
    }

    /**
     * 查询商品库存帐 去重的仓库库区
     *
     */
    public List<Stock> findStockDistinctForLocation(SimpleRequest request) {
        JSONObject param = request.getJO();
        if (Tools.isNull(param.getString("locationId"))) {
             return stockRepository.findAllStock();
        }else {
            Location location = new Location();
            location.setId(param.getString("locationId"));
            return stockRepository.findAllByLocation(location);
        }
    }

    /**cm.stock.updateProductSafetyStock
     * 定时任务跑安全库存缺口报表
     *
     */
    public void computeSafetyStockGap (SimpleRequest request) {
        //调用存储过程执行类
        LocalDate date = LocalDate.now();
        //线程安全的时间格式化对象 begin
        //   -- Call the procedure
        //   sum_stock_safety_gap(p_date => :p_date);
        // end;
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        String dateStr = formatter.format(date);
        JSONObject param = new JSONObject();
        Query query = entityManager.createNativeQuery("BEGIN SUM_STOCK_SAFETY_GAP(P_DATE => :P_DATE); END ;").setParameter("P_DATE",date);
        int i = 0;
        try {
            i = query.executeUpdate();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (i <= 0) {
            throw new RuntimeException("执行失败");
        }else {
            List<SafetyStockGap> list = null;
            try {
                list = safetyStockGapRepository.findAllByStatisticsDate(format.parse(dateStr));
            } catch (ParseException e) {
                System.out.println(e);
            }
            JSONArray array = new JSONArray();
            array = list.stream().map(safetyStockGap -> {
                JSONObject object = new JSONObject();
                Product product  = safetyStockGap.getProduct();
                Location location = safetyStockGap.getLocation();
                object.put("TJRQ", safetyStockGap.getStatisticsDate());
                object.put("CKKQID", location.getId());
                object.put("CKKQMC", location.getName());
                object.put("CKID", location.getPhyDepot().getId());
                object.put("CKMC", location.getPhyDepot().getName());
                object.put("SPID", product.getId());
                object.put("SPMC", product.getName());
                object.put("AQKC", safetyStockGap.getSafetyStockQty());
                object.put("ZDXL", safetyStockGap.getTheMinQty());
                object.put("KYS", safetyStockGap.getUsableQty());
                object.put("DHZT", safetyStockGap.getPurchasingWayQty());
                object.put("ZCSHZT", safetyStockGap.getTransferReceiveQty());
                object.put("BCQKSL", safetyStockGap.getGapQty());
                object.put("HJSL", safetyStockGap.getTotalQty());
                // object.put("CGSL", safetyStockGap.getLocation());
                object.put("YRKSL", safetyStockGap.getWarehousingQty());
//                object.put("ZHRKRQ_SJ", safetyStockGap.getLastInDate());
//                object.put("ZCSL", safetyStockGap.getDumpQty());
                object.put("XKJDHBJ", 1);
                // object.put("ZCRKRQ_SJ", safetyStockGap.getLocation());
                // object.put("CGJHSL", safetyStockGap.getLocation());
                return  object;
            }).collect(Collectors.toCollection(JSONArray::new));
            param.put("TABLE","RPT_AQKCQKRBB");
            param.put("items",array);
            piManager.synErp("scmKckzService.insertAQKCQK",param);
        }
    }

    /**
     * 批量更新安全库存
     *
     */
    public String  batchUpdateProductSafetyStock(SimpleRequest request){
        checkItemIsNotNull(request);
        List<Stock> list = request.getA("safetyStockItems",Stock.class);
        list.forEach(
                item -> this.invoke("cm.stock.updateProductSafetyStock", MapUtil.mapped(item))
        );
        return "更新成功!!!";
    }

    /**
     * 检查明细不能为空
     * @param request 数据
     */
    private void checkItemIsNotNull(SimpleRequest request) {
        JSONObject object = request.getJO();
        JSONArray safetyStockItems = object.getJSONArray("safetyStockItems");
        if (safetyStockItems == null || safetyStockItems.size() == 0) {
            throw new RuntimeException("安全库存明细不能为空!!!");
        }
    }

    /**
     * 批量清空安全库存
     *
     */
    public String batchClearProductSafetyStock(SimpleRequest request){
        checkItemIsNotNull(request);
        List<Stock> list = request.getA("safetyStockItems",Stock.class);
        list.forEach(
                item -> {
                    createSafetyStockLogger(item,null,getPerson(request));
                    Product product = item.getProduct();
                    ProductUom uom = product.getUom();
                    Location location = item.getLocation();
                    PartyRole company = location.getCompany();
                    double theMinQty = 0;
                    double theMaxQty = 0;
                    double safetyStockQty =0;
                    setProductSafetyStock(product,location,uom,theMaxQty,theMinQty,safetyStockQty,company);
                    SpringManager.getBean(ApesLoggerService.class).sendSynEvent("event:ir.logger.create", MapUtil.mapper(
                            "businessType", item.getInnerBusinessType(),
                            "businessKey", item.getInnerBusinessKey(),
                            "operate", "一键清零安全库存",
                            "workPostName",request.getSession().getAttribute("workPostName", String.class),
                            "operator_id", request.getSession().getUserId()));
                }
        );
        return "清零成功!!!";
    }
    /**
     * 手动查询库存帐逻辑
     *
     */
    public List<Map<String, Object>> findAllManual(SimpleRequest request){
        JSONObject object = request.getJO();

        // List<Map<String, Object>> filters = new ArrayList<>();
        // if (Objects.nonNull(object.get("location"))) {
        //     JSONArray array = object.getJSONArray("location");
        //     List<String> list = getQueryList(array);
        //     Map<String, Object> map = new HashMap<>();
        //     map.put("field", "location.id");
        //     map.put("operator", "IN");
        //     map.put("value",list);
        //     filters.add(map);
        // }
        // if (Objects.nonNull(object.get("productGroup"))) {
        //     Map<String, Object> map = new HashMap<>();
        //     JSONArray array = object.getJSONArray("productGroup");
        //     List<String> list = getQueryList(array);
        //     map.put("field", "product.productGroup.id");
        //     map.put("operator", "IN");
        //     map.put("value", list);
        //     filters.add(map);
        // }
        // if (Objects.nonNull(object.get("classify"))) {
        //     Map<String, Object> map = new HashMap<>();
        //     map.put("field", "product.classify.id");
        //     map.put("operator", "LIKE");
        //     map.put("value", object.getString("classify"));
        //     filters.add(map);
        // }
        // if (Objects.nonNull(object.get("category"))) {
        //     Map<String, Object> map = new HashMap<>();
        //     JSONArray array = object.getJSONArray("category");
        //     List<String> list = getQueryList(array);
        //     map.put("field", "location.id");
        //     map.put("operator", "IN");
        //     map.put("value",list);
        //     filters.add(map);
        // }
        // if (Objects.nonNull(object.get("product"))) {
        //     Map<String, Object> map = new HashMap<>();
        //     JSONObject obj = object.getJSONObject("product");
        //     map.put("field", "product.id");
        //     map.put("operator", "IN");
        //     map.put("value", obj.getString("id"));
        //     filters.add(map);
        // }
        if (Objects.isNull(object.get("product")) && Objects.isNull(object.get("category"))
                && Objects.isNull(object.get("location")) && Objects.isNull(object.get("productGroup"))
                && Objects.isNull(object.get("classify")) && Objects.isNull(object.get("brand")) && Objects.isNull(object.get("company"))) {
            throw new RuntimeException("查询条件不能为空!!!");
        }
        JSONArray resultArray = SpringManager.getBean(ProductStockDao.class).queryStockSafetyQty(object);
        // List<Stock> list = stockRepository.findAll(
        //         JpaDsl.toCriteria(filters)
        // );
        //过滤不是品类直供范围得商品
//        resultArray = resultArray.stream().filter(
//                item -> {
//                    JSONObject data = (JSONObject) item;
//                    Product product = productRepository.findOne(data.getString("PRODUCT_ID"));
//                    return categorySupplyRangeService.checkInRange(product.getClassify(),product.getBrand());
//                }
//        ).collect(Collectors.toCollection(JSONArray::new));
        //拼装前端展示数据
        List result = resultArray.stream().map(
            item -> {
                JSONObject jsonObject = (JSONObject) item;
                return  MapUtil.mapper(
                        "id",jsonObject.getString("ID"),
                        "location",MapUtil.mapper("id",jsonObject.getString("LOCATION_ID"),"name",jsonObject.getString("LOCATION_NAME")),
                        "product",MapUtil.mapper("id",jsonObject.getString("PRODUCT_ID"),"name",jsonObject.getString("PRODUCT_NAME")),
                        "classify",MapUtil.mapper("id",jsonObject.getString("CLASSIFY_ID"),"name",jsonObject.getString("CLASSIFY_NAME")),
                        "brand",MapUtil.mapper("id",jsonObject.getString("BRAND_ID"),"name",jsonObject.getString("BRAND_NAME")),
                        "safetyStockQty",jsonObject.getDoubleValue("SAFETY_STOCK_QTY"),
                        "theMaxQty",jsonObject.getDoubleValue("THE_MAX_QTY"),
                        "theMinQty",jsonObject.getDoubleValue("THE_MIN_QTY")
                );
            }
        ).collect(Collectors.toList());
        if (result.size() == 0 ){
            throw new RuntimeException("未找到满足条件得数据!!!");
        }
        return  result;
    }

    private List<String> getQueryList(JSONArray array) {
        return array.stream().map(
                (Object item) -> {
                    JSONObject data = (JSONObject) item;
                    return data.getString("id");
                }
        ).collect(Collectors.toList());
    }

    /**
     * 定制导入安全库存
     *
     */
    public JSONObject importSafetyStock(SimpleRequest request){
        //Excel信息
        JSONObject importData = request.get("importData");
        JSONArray field = (JSONArray) request.get("data");
        JSONObject exclObject = new JSONObject();                  //返回值

        JSONArray exclArr = new JSONArray();            //行内容
        JSONArray headerName = new JSONArray();         //头部
        JSONArray fieldName = new JSONArray();          //field
        for (Object o : field) {
            JSONObject webTab = (JSONObject) o;
            headerName.add(webTab.get("name"));
            fieldName.add(webTab.get("field"));
        }



        //业务逻辑
        importData.forEach((k, v) -> {

            //获得value ===Excel内容
            JSONArray excl = (JSONArray) v;
            JSONArray header = (JSONArray) excl.get(0); //首行数据
            String productId,companyId,locationId;
            for (int i = 0; i < excl.size(); i++) {  //遍历每一行

                if (i == 0) { //跳过首行
                    exclArr.add(headerName);
                    continue;
                }

                JSONArray lineLong = (JSONArray) excl.get(i); //当前行
                if (lineLong.size() == 0) {
                    continue; //跳过空白行
                }
                //合并2个list为一个map,KEY为首行数据，Value为当前行数据
                Map<String, String> rst = IntStream.range(0, header.size()).collect(HashMap<String, String>::new,
                        (m, key) -> m.put(header.getString(key).replace(";", ""),
                                key >= lineLong.size() ? "" : (lineLong.getString(key) == null ? null : lineLong.getString(key).trim().replaceAll("[\\s]{2,}", " "))),
                        (m1, m2) -> {
                        });
                this.judgeTitle(rst);
                productId = rst.get("商品");
                locationId = rst.get("仓库编码");
                List<Stock> list = stockRepository.findAll(
                        JpaDsl.toCriteriaByEq("product.id",productId,"location.id",locationId)
                );
                Stock stock = new Stock();
                if (list.size() >0) {
                    stock = list.get(0);
                }else {
                    Product product = SpringManager.getBean(ProductRepository.class).findOne(productId);
                    checkWhetherNull(product,"商品", productId);
                    Location location = SpringManager.getBean(LocationRepository.class).findOne(locationId);
                    checkWhetherNull(location,"仓库编码", locationId);
                    PartyRole company = SpringManager.getBean(PartyRoleRepository.class).findOne(location.getCompany().getId());
                    checkWhetherNull(company,"公司", location.getCompany().getId());
                    stock.setCompany(company);
                    stock.setLocation(location);
                    stock.setProduct(product);
                    stock = SpringManager.getBean(StockRepository.class).saveAndFlush(stock);

                }
        /*        if (!categorySupplyRangeService.checkInRange(stock.getProduct().getClassify(),stock.getProduct().getBrand()) ){
                    continue;
                }*/
                //用一个有序的map 封装数据, 防止乱序
                Map<String, Object> map = new LinkedHashMap<>();
                map.put("序号", "");
                map.put("仓库编码", MapUtil.mapped(stock.getLocation()));
                map.put("仓库名称", MapUtil.mapped(stock.getLocation()));
                map.put("商品", MapUtil.mapped(stock.getProduct()));
                map.put("商品名称", MapUtil.mapped(stock.getProduct()));
                map.put("商品分类", MapUtil.mapped(stock.getProduct().getClassify()));
                map.put("品牌", MapUtil.mapped(stock.getProduct().getBrand()));
                map.put("安全库存", Double.parseDouble(rst.get("安全库存")));
                map.put("最高限量", Double.parseDouble(rst.get("最高限量")));
                map.put("最低限量", Double.parseDouble(rst.get("最低限量")));
                //将map 转换成 list 利用stream流 引用Map类的getValue方法,弄成一个list
                List<Object> valueList = new ArrayList<>(map.values());
                exclArr.add(valueList);
            }
            exclObject.put(k, exclArr);
        });


        return exclObject;
    }

    /**
     * 判断列头是否有异常
     * @param rst
     */
    private void judgeTitle(Map rst) {
        if (StringUtils.isEmpty(rst.get("商品")) || StringUtils.isEmpty(rst.get("仓库编码")) || StringUtils.isEmpty(rst.get("安全库存"))
                || StringUtils.isEmpty(rst.get("最高限量")) || StringUtils.isEmpty(rst.get("最低限量"))) {
            throw new RuntimeException("请重新导出文件导入，请按照导出文件导入！");
        }
    }

    private  void  checkWhetherNull(Object object, String bean, String value) {
        if (object == null){
            throw new RuntimeException("未找到对应" + bean + ", 编码: " + value);
        }
    }

    /************************************************************************************************/

    /**
     * 老框架回写转入制单数量
     * @param simpleRequest
     */
    public void putTransferInBillingQty(SimpleRequest simpleRequest) {
        JSONObject getJO = simpleRequest.getJO();
        JSONArray itemArray = getJO.getJSONArray("item");
        itemArray.forEach(item -> {
            JSONObject singleItem = (JSONObject) item;
            String spid = singleItem.getString("SPID");
            Location location = locationRepository.findOne(singleItem.getString("CKKQID"));
            Product product = productRepository.findOne(spid);
            ProductUom uom = productUomRepository.findAll(JpaDsl.toCriteriaByEq("name", singleItem.getString("JLDW"), "uomCategory.id", spid)).get(0);
            PartyRole company = SpringManager.getBean(PartyRoleRepository.class).findOne(singleItem.getString("GSID"));
            double js = singleItem.getDoubleValue("JS");
            try {
                this.setProductTransferInBilling(product, location, uom, js, company);
            } catch (Exception e) {
                SpringManager.getBean(OutboxSubscriberService.class).commonSendRequest("local.productAccountService.setOldSysLog", "oldWriteDumpQty: " + location.getId() + "; " + product.getId(), singleItem);
            }
        });

    }

    @OutboxSubscriber(id = "local.productAccountService.setOldSysLog", name = "转入制单数量不能小于0，抛错")
    private void setOldSysLog(OutboxPayloadWrapper outboxPayloadWrapper) {
        piManager.synErp("scmZcddService.insertLog", outboxPayloadWrapper.getPayload());
    }

    /**
     * 老框架回写转储发货在途
     * @param simpleRequest
     */
//    public void putTransferIssueQty(SimpleRequest simpleRequest) {
//        JSONObject getJo = simpleRequest.getJO();
//        String gSPID = getJo.getString("SPID");
//        Product product = productRepository.findOne(gSPID);
//        List<Location> locationList = this.ckkqidGetLocation(getJo.getString("CKKQID"));
//        List<ProductUom> productUomList = productUomService.jldwGetProductUom(getJo.getString("JLDW"), getJo.getString("SPID"));
//        List<PartyRole> partyRoleList = contrastPartyCodeService.gsidGetContrastPartyCode(getJo.getString("GSID"));
//        this.setProductTransferIssue(product, locationList.get(0), productUomList.get(0), getJo.getDouble("ZCJS"), partyRoleList.get(0));
//    }

    /**
     * 老框架回写转储收货在途
     * @param simpleRequest
     */
//    public void putTransferReceiveQty(SimpleRequest simpleRequest) {
//        JSONObject getJo = simpleRequest.getJO();
//        String gSPID = getJo.getString("SPID");
//        Product product = productRepository.findOne(gSPID);
//        List<Location> locationList = this.ckkqidGetLocation(getJo.getString("CKKQID"));
//        List<ProductUom> productUomList = productUomService.jldwGetProductUom(getJo.getString("JLDW"), getJo.getString("SPID"));
//        List<PartyRole> partyRoleList = contrastPartyCodeService.gsidGetContrastPartyCode(getJo.getString("GSID"));
//        this.setProductTransferIssue(product, locationList.get(0), productUomList.get(0), getJo.getDouble("ZCJS"), partyRoleList.get(0));
//    }

    /**
     * 根据仓库库区获取库区信息
     * @param pCKKQID
     * @return
     */
    public List<Location> ckkqidGetLocation(String pCKKQID) {
        List<Location> locationList = locationRepository.findAll(JpaDsl.toCriteriaByEq("origin", pCKKQID));
        if (locationList.size() == 0) {
            throw new RuntimeException("未找到仓库库区外部ID：" + pCKKQID);
        }
        return locationList;
    }

}
